package cn.com.gatico.login.auth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.*;


@Configuration
@EnableAuthorizationServer
public class UserAuthenticationAdapter extends AuthorizationServerConfigurerAdapter {

    private final Logger logger = LoggerFactory.getLogger(UserAuthenticationAdapter.class);
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private ClientDetailsService clientDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String encodedPassword) {
                return charSequence.toString().equals(encodedPassword);
            }
        };
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.allowFormAuthenticationForClients()
                //校验ClientId
                //.addAuthenticationProvider()
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .passwordEncoder(passwordEncoder)
        ;
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() {
        List<AuthenticationProvider> authenticationProviderList = new ArrayList<>();
        authenticationProviderList.add(new AuthenticationProvider() {

            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                String username = authentication.getPrincipal().toString();
                String password = authentication.getCredentials().toString();
                Map<String, Object> details = (Map<String, Object>) authentication.getDetails();
                logger.info("登录信息：[{}]", details.toString());
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (userDetails != null) {
                    if (passwordEncoder.matches(password, userDetails.getPassword())) {
                        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
                    } else {
                        throw new BadCredentialsException("登录失败：密码错误");
                    }
                } else {
                    throw new UsernameNotFoundException("登录失败: 用户不存在");
                }
            }

            @Override
            public boolean supports(Class<?> aClass) {
                return true;
            }
        });
        ProviderManager authenticationManager = new ProviderManager(authenticationProviderList);
        authenticationManager.setAuthenticationEventPublisher(new AuthenticationEventPublisher() {
            @Override
            public void publishAuthenticationSuccess(Authentication authentication) {
                logger.info("用户名：{} 登录成功", authentication.getName());
            }

            @Override
            public void publishAuthenticationFailure(AuthenticationException e, Authentication authentication) {
                logger.info("用户名：{}，错误信息[{}]", authentication.getName(), e.getMessage());
            }
        });
        return authenticationManager;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        List<UserDetails> userDetails = new ArrayList<>();
        userDetails.add(new User("user", "user", Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))));
        userDetails.add(new User("admin", "admin", Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"))));

        return new InMemoryUserDetailsManager(userDetails) {
            @Override
            public UserDetails loadUserByUsername(String username) {
                try {
                    return super.loadUserByUsername(username);
                } catch (UsernameNotFoundException ignored) {
                    return null;
                }
            }
        };
    }

    @Bean
    public InMemoryClientDetailsService clientDetailsService() {
        InMemoryClientDetailsService inMemoryClientDetailsService = new InMemoryClientDetailsService();
        Map<String, BaseClientDetails> clientDetailsStore = new HashMap<>();
        BaseClientDetails user = new BaseClientDetails();
        user.setClientId("user");
        user.setClientSecret("clientsecretuser");
        user.setScope(Arrays.asList("read", "write", "trust"));
        user.setAuthorizedGrantTypes(Arrays.asList("password", "authorization_code", "refresh_token", "implicit"));
        user.setAccessTokenValiditySeconds(5 * 60 * 1000);
        user.setRefreshTokenValiditySeconds(10 * 60 * 1000);
        clientDetailsStore.put(user.getClientId(), user);
        BaseClientDetails admin = new BaseClientDetails();
        admin.setClientId("admin");
        admin.setClientSecret("clientsecretadmin");
        admin.setScope(Arrays.asList("read", "write", "trust"));
        admin.setAuthorizedGrantTypes(Arrays.asList("password", "authorization_code", "refresh_token", "implicit"));
        admin.setAccessTokenValiditySeconds(5 * 60 * 1000);
        admin.setRefreshTokenValiditySeconds(10 * 60 * 1000);
        clientDetailsStore.put(admin.getClientId(), admin);
        inMemoryClientDetailsService.setClientDetailsStore(clientDetailsStore);
        return inMemoryClientDetailsService;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                //检验用户
                .userDetailsService(userDetailsService)
                //拦截器
                //.addInterceptor()
                .allowedTokenEndpointRequestMethods(HttpMethod.POST)
                .accessTokenConverter(new DefaultAccessTokenConverter())
                // 默认true:使用refresh_token刷新token时，不刷新refresh_token
                // false:使用refresh_token刷新token时，同时刷新refresh_token
                .reuseRefreshTokens(false)
                .tokenStore(new InMemoryTokenStore());
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().clients(clientDetailsService);
    }


}
